package cn.buk.tms.flight.service;

import cn.buk.api.szkj.dto.eterm.PnrInfoFn;
import cn.buk.api.szkj.dto.eterm.PnrInfoPrice;
import cn.buk.api.szkj.dto.eterm.request.*;
import cn.buk.api.szkj.dto.eterm.response.CreatePnrResponse;
import cn.buk.api.szkj.dto.eterm.response.PnrPatResponse;
import cn.buk.api.szkj.dto.eterm.response.RtpnrResponse;
import cn.buk.api.szkj.service.SzkjEtermService;
import cn.buk.common.util.DateUtil;
import cn.buk.common.util.DecimalUtil;
import cn.buk.qms.service.BaseFlightOrderService;
import cn.buk.qms.service.FlightOrderService;
import cn.buk.tms.common.dto.base.BaseDto;
import cn.buk.tms.common.dto.base.BasePriceDto;
import cn.buk.tms.dto.EnterpriseConfigDto;
import cn.buk.tms.dto.OrderConfigDto;
import cn.buk.tms.entity.misc.*;
import cn.buk.tms.entity.order.flight.FlightOrder;
import cn.buk.tms.entity.order.flight.FlightOrderFlight;
import cn.buk.tms.entity.order.flight.FlightOrderPassenger;
import cn.buk.tms.flight.dao.KeyCustomerPolicyDao;
import cn.buk.tms.flight.dto.response.FlightOrderResponseDto;
import cn.buk.tms.flight.request.FlightOrderRequestDto;
import cn.buk.tms.flight.util.FlightOrderUtil;
import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

import static cn.buk.common.Constant.*;
import static cn.buk.common.JsonResult.STATUS_OK;
import static cn.buk.tms.common.constant.TmsConstant.FLIGHT_AUTO_TICKETING_BSP;
import static cn.buk.tms.common.constant.TmsConstant.FLIGHT_AUTO_TICKETING_NDC_CZ;
import static cn.buk.tms.common.constant.TmsOrderConstant.ORDER_PRICE_VERIFY_STATE_NONE;
import static java.nio.charset.StandardCharsets.UTF_8;

@Service
public class FlightOrderCheckServiceImpl extends BaseFlightOrderService implements FlightOrderCheckService {

  private static final Logger logger = LogManager.getLogger(FlightOrderCheckServiceImpl.class);

  @Autowired
  private KeyCustomerPolicyDao keyCustomerPolicyDao;

  @Autowired
  private FlightOrderService flightOrderService;

  @Override
  public void checkPriceOfFlightOrders(final int enterpriseId, final String username) {
    //查找需要验价的订单
    var flightOrders = flightOrderDao.searchNeedPriceCheckFlightOrders(enterpriseId, DateUtil.getYesterday());

    for (var flightOrder : flightOrders) {
      checkFlightOrderPrice(enterpriseId, username, flightOrder);
    }
  }

  /**
   * 校验机票订单的价格
   * @param enterpriseId 企业id
   * @param username 用户名
   * @param flightOrder 简略订单信息
   */
  private void checkFlightOrderPrice(final int enterpriseId, final String username, FlightOrder flightOrder) {
    if (flightOrder.getPriceChecked() != ORDER_PRICE_VERIFY_STATE_NONE) {
      //订单的价格校验状态不是“待校验”
      return;
    }

    if (flightOrder.getIntlTicket() == 1) {
      //国际票，不验价
      return;
    }

    //判断所属客户是否停用了
    if (isCustomerStopped(flightOrder.getCustomer())) {
      doSendAlert(enterpriseId, "机票订单(" + flightOrder.getOrderNo() + ")所属客户(" + flightOrder.getCustomer().getVipName() + ")处于停用状态。", null, EntDepartment.DEPT_ADMINISTRATOR);
      return;
    }

    //获取订单详情
    FlightOrder orderDetail = flightOrderDao.getOrderDetailById(flightOrder.getId());

    // 判断订单中的乘客是否全部为成人
    if (!FlightOrderUtil.isAdultFlightOrder(orderDetail)) {
      return;
    }

    //判断订单中的航班日期是否已过
    if (FlightOrderUtil.isFlightOrderExpired(orderDetail)) {
      return;
    }

    //客服导入的订单不需要校验价格和生成编码
    //非后台白屏预订的，只有企业客户自助下单的订单需要校验价格
    //后台白屏预订的, 也需要生成编码和验价
    if (flightOrder.getBookingChannel() == 0) {
      return;
    } else if (flightOrder.getBookingChannel() == -1) {
      User user = userDao.getUserByUsername(flightOrder.getOperator());
      if (user == null || user.getParentEnterprise() != enterpriseId) {
        return;
      }
    }

    executeCheckPriceOfFlightOrder(orderDetail, username);
  }

  /**
   * 执行校验机票订单的价格
   */
  private void executeCheckPriceOfFlightOrder(final FlightOrder orderDetail, final String username) {
    //查看订单中是否有编码，如果没有，则预订编码
    if (isCustomerTestMode(orderDetail.getCustomer())) {
      //测试模式的客户，单独走一个验价流程, 目前直走CZ NDC
      checkFlightTestOrderPrice(orderDetail, username);
      return;
    }

    //TODO 如果是CZ的订单，则调用cz-ndc进行验价
    if (isCzOrder(orderDetail)) {
      checkFlightTestOrderPrice(orderDetail, username);
      return;
    }



    //如果是CZ的航班，则走

    if (StringUtils.isBlank(orderDetail.getPnrNo())) {
      createPnr(orderDetail, username);
      return;
    }

    if (orderDetail.getGpTicket() == 1) {
      //GP机票验价
      executeCheckFlightOrderPriceWithGp(orderDetail, username);
    } else {
      final String policyCode = StringUtils.trimToEmpty(orderDetail.getPolicyCode());

      if (policyCode.length() == 0 ) {
        //只是普通的公布运价订单，校验航司销售价是否一致即可
        executeCheckFlightOrderPriceWithNormalPrice(orderDetail, username);
      } else {
        //校验大客户政策订单
        executeCheckFlightOrderPriceWithPolicyCode(orderDetail, username);
      }

      if (orderDetail.getBookingChannel() == -1) {
        //客户自助下单了，自动设置订单为BSP供应商
        autoSetFlightOrderTicketingFeeAndBspSupplier(username, orderDetail);
      }
    }
  }

  /**
   * 校验机票测试订单的价格，方便测试
   */
  private void checkFlightTestOrderPrice(FlightOrder flightOrder, String username) {
    //使用NDC的
    final int enterpriseId = flightOrder.getEnterpriseId();
    final int orderId = flightOrder.getId();
    var fop = offerCzNdcPrice(enterpriseId, username, flightOrder);

    if (fop.getErrcode() == 0) {
      //保存到redis
      final String key = String.format(CZ_NDC_SHOPPING_RESPONSE, enterpriseId, fop.getShoppingResponseId());
      final String value = JSON.toJSONString(fop);

      redisDao.setAndExpire(key, value.getBytes(UTF_8), 60L * 10);
    } else {
      //标识该订单获取CZ NDC验价时出错，不再用NDC开票
      flightOrderDao.appendFlightOrderLog(enterpriseId, username, orderId, fop.getErrmsg());

      final String key = String.format(CZ_NDC_ORDER_KEY, enterpriseId, flightOrder.getOrderNo());
      final String value = JSON.toJSONString(fop.getErrmsg());

      redisDao.setAndExpire(key, value.getBytes(UTF_8), 60L * 10);
      return;
    }


    String remark = String.format("CZ_NDC_自动验价: %s (订单号：%s)", "有返回价格", flightOrder.getOrderNo());
    doSendAlert(enterpriseId, remark, "Dai", 0);

    //确定价格，创建订单
    List<String> itemCodes = new ArrayList<>();
    for (var fareDto : fop.getSegments().get(0).getFareInfoDtos()) {
      //有更便宜的价格，发送信息提醒
      if (flightOrder.getPrice() > 0 && flightOrder.getPrice() > fareDto.getBaseAmount()) {
        //发出提醒
        var content = String.format("订单(%s, %s, %f)CZ_NDC询价时看到更低价格(%f, %s)", flightOrder.getOrderNo(),
                flightOrder.getOperator(), flightOrder.getPrice(), fareDto.getBaseAmount(), fareDto.getFareBasisCode());
        sendFlightOrderAlertToWwAppChat(enterpriseId, content);
      }

      if (fareDto.getBaseAmount() == flightOrder.getParvalue() || fareDto.getBaseAmount() == flightOrder.getPrice()) {
        //航司的卖价等于票面价或者销售价，我司可能在航司的票面价的基础商加价出售 yfdai 2021-10-01
        //还有一种可能，就是没有匹配的价格：这样的情况一般是低价票，在之前预订编码时已经锁定了价格，现在再去查询价格就只有高价了，所以只能用BSP出票。
        itemCodes.add(fareDto.getOfferItemId());
        break;
      }
    }

    if (itemCodes.isEmpty()) {
      //未反馈匹配价格，停止用CZ NDC出票
      //标识该订单获取CZ NDC验价时出错，不再用NDC开票
      flightOrderDao.appendFlightOrderLog(enterpriseId, username, flightOrder.getId(), "CZ_NDC_未找到匹配价格");

      final String key = String.format(CZ_NDC_ORDER_KEY, enterpriseId, flightOrder.getOrderNo());
      final String value = JSON.toJSONString(fop.getErrmsg());

      redisDao.setAndExpire(key, value.getBytes(UTF_8), 60L * 10);

      return;
    }

    final String shoppingResponseId = fop.getShoppingResponseId();

    var jr = flightOrderService.confirmFlightOrderPrice(enterpriseId, username, flightOrder.getId(), shoppingResponseId, itemCodes);
    if (!STATUS_OK.equalsIgnoreCase(jr.getStatus())) {
      doSendAlert(enterpriseId, "CZ_NDC_自动出票: " + jr.getErrmsg(), "Dai", 0);

      final String key = String.format(CZ_NDC_ORDER_KEY, enterpriseId, flightOrder.getOrderNo());
      final String value = JSON.toJSONString(jr.getErrmsg());

      redisDao.setAndExpire(key, value.getBytes(UTF_8), 60L * 10);
    } else {
      flightOrderService.updateFlightOrderSupplierType(enterpriseId, username, flightOrder.getId(), FLIGHT_AUTO_TICKETING_NDC_CZ);
    }
  }

  /**
   * 自动设置机票订单的出票服务费和BSP供应商
   */
  private void autoSetFlightOrderTicketingFeeAndBspSupplier(final String username, final FlightOrder flightOrder) {
    final int enterpriseId = flightOrder.getEnterpriseId();
    final int orderId = flightOrder.getId();

    //1. 自动设置服务费
    setFlightOrderTicketingFee(username, flightOrder.getId());


    //2. 判断自动开票开关的设置
    OrderConfigDto orderConfig = enterpriseService.searchOrderConfig(enterpriseId);
    if (orderConfig.getAutoTicketSwitch() == 0) {
      return;
    } else if (orderConfig.getAutoTicketSwitch() == 1) {
      //非工作时间自动出票
      EnterpriseConfigDto entConfig = enterpriseService.searchEntConfig(enterpriseId);
      final int dayOfWeek = DateUtil.getDayOfWeek(DateUtil.getCurDate());
      final String workDays = entConfig.getWorkDay();
      if (DateUtil.isInEntWorkDay(dayOfWeek, workDays)
              && DateUtil.isInWorkTime(entConfig.getWorkBeginTime(), entConfig.getWorkEndTime())) {
        return;
      }
    }

    // 3. 设置机票订单为BSP供应商, 方便后续自动开票模块进行工作
    if (flightOrder.getSupplier() != null) {
      //已设置了供应商
      return;
    }

    this.doUpdateFlightOrderSupplierType(enterpriseId, username, orderId, FLIGHT_AUTO_TICKETING_BSP);

  }

  /**
   * 根据订单内容生成编码
   */
  private synchronized void createPnr(final FlightOrder orderDetail, final String username) {
    //目前只支持成人订单生成编码
    if (!FlightOrderUtil.isAdultFlightOrder(orderDetail)) {
      return;
    }

    if (orderDetail.getCreateTime() != null && DateUtil.getPastMinutes(orderDetail.getCreateTime()) >= 15) {
      //创建时间超过15分钟的订单不再预订编码
      return;
    }

    final String key = String.format("create_pnr_%d_%s", orderDetail.getEnterpriseId(), orderDetail.getOrderNo());
    //控制频率，5分钟只尝试一次创建pnr(定时任务目前每30秒执行一次，有时候会跨越30秒，导致两次的定时任务会同时对一个订单去生成编码)
    if (redisDao.incrApiCallCount(key, 60L * 5, 1) == 0) {
      doSendAlert(orderDetail.getEnterpriseId(), username + ": " + key, "Dai", 0);

      return;
    }

    //预订编码
    PnrCreateRequest request = createPnrCreateRequest(orderDetail);

    final int enterpriseId = orderDetail.getEnterpriseId();
    final int orderId = orderDetail.getId();
    final String orderNo = orderDetail.getOrderNo();
    final int queryId = userDao.createEtermCmdLog(enterpriseId, username, "生成编码", orderDetail.getOrderNo());

    SzkjEtermService szkjEtermService = createSzkjEtermService(enterpriseId);

    CreatePnrResponse response = szkjEtermService.createPnr(getEtermUsername(enterpriseId), getEtermPassword(enterpriseId), queryId, request);
    userDao.updateEtermCmdLog(queryId, response.getRequestStr(), response.getResponseStr());

    if (response.getCode() == 1) {
      //接口调用成功
      final String pnrNo = response.getPnrInfo().getPnr();
      if (flightOrderDao.updatePnr(enterpriseId, username, orderId, pnrNo) == 1) {
        // 提取编码内容并保存
        doRefreshFlightOrderPnrDetail(enterpriseId, username, orderId, orderNo, pnrNo);
      }
    } else {
      //生成编码失败
      flightOrderDao.appendFlightOrderLog(enterpriseId, username, orderId, response.getCode() + ": " + response.getMsg());
      doSendAlert(enterpriseId, "机票订单(" + orderNo + ")预订编码失败：" + response.getMsg(), null, EntDepartment.DEPT_TICKETING);
    }
  }

  /**
   * GP机票订单验价
   */
  private void executeCheckFlightOrderPriceWithGp(final FlightOrder flightOrder, final String username) {
    final int enterpriseId = flightOrder.getEnterpriseId();
    final int orderId = flightOrder.getId();
    final String pnrNo = flightOrder.getPnrNo();

    PnrPatResponse response = execApiPatPnr(enterpriseId, username, pnrNo, "GP", flightOrder.getOrderNo(), "编码GP验价");

    if (response.getPnrInfo() == null
            || response.getPnrInfo().getFns().isEmpty()) {
      //验价未通过
      String remark = "编码(" + pnrNo + ")验价失败(调用记录ID：" + response.getQueryId() + ")";
      doSendAlert(enterpriseId, remark + ": " + response.getMsg(), "Dai", 0);
      flightOrderDao.updateFlightOrderPriceChecked(enterpriseId, orderId, username, 4, remark);
    }

    final FlightOrderResponseDto dto = FlightOrderResponseDto.createByFlightOrder(flightOrder);

    PnrInfoFn priceInfo = response.getPnrInfo().getFns().get(0);

    //税
    final double tax = priceInfo.getTax();
    final double parValue = priceInfo.getPrice();

    FlightOrderRequestDto newDto = new FlightOrderRequestDto();
    newDto.setPsgCount(dto.getPsgCount());
    newDto.setAdultCount(dto.getAdultCount());

    BasePriceDto newAdultPrice = new BasePriceDto();
    newAdultPrice.setPrice(priceInfo.getPrice());
    newAdultPrice.setParValue(parValue);
    newAdultPrice.setTax(tax);

    newDto.setAdultPrice(newAdultPrice);

    int retCode = flightOrderDao.updatePrice(enterpriseId, username, orderId, newDto);
    if (retCode == 1) {
      //验价成功
      flightOrderDao.updateFlightOrderPriceChecked(enterpriseId, orderId, username, 1, "编码GP验价成功(调用记录ID：" + response.getQueryId() + ")");
      //刷新编码内容
      this.refreshFlightOrderPnrDetail(enterpriseId, username, flightOrder);
    }
  }

  /**
   * 校验普通政策的机票订单价格
   */
  private void executeCheckFlightOrderPriceWithNormalPrice(final FlightOrder orderInfo, final String username) {
    //尝试将普通订单转换为大客户注册订单
    var cr  = convertFlightOrderNormalPrice2PolicyCode(orderInfo, username);
    if (cr.getErrcode() == 0) {
      //已转换
      return;
    } else {
      doSendAlert(orderInfo.getEnterpriseId(),
              orderInfo.getOrderNo() + ": convertFlightOrderNormalPrice2PolicyCode - " + cr.getErrcode() + ", " + cr.getErrmsg(),
              "Dai", 0);
    }


    final int enterpriseId = orderInfo.getEnterpriseId();
    final int orderId = orderInfo.getId();
    final String pnrNo = orderInfo.getPnrNo();

    FlightOrder o = flightOrderDao.getOrderDetailById(orderId);
    if (!FlightOrderUtil.isAdultFlightOrder(o)) {
      //不是成人订单
      return;
    }

    if (o.getPriceChecked() != 0) {
      //价格已经校验过
      logger.warn("Order({})'s price is checked({}, {}).", o.getOrderNo(), o.getPriceChecked(), o.getPriceCheckedTime());
      return;
    }

    FlightOrderResponseDto dto = FlightOrderResponseDto.createByFlightOrder(o);
    BasePriceDto adultPrice = dto.getAdultPrice();

    PnrPatResponse response = execApiPatPnr(enterpriseId, username, pnrNo, null, o.getOrderNo(), "编码验价");
    if (response.getCode() != 1) {
      //接口调用失败
      //验价未通过
      flightOrderDao.updateFlightOrderPriceChecked(enterpriseId, orderId, username, 4, "编码验价失败(调用记录ID：" + response.getQueryId() + ")");
      doSendAlert(enterpriseId, "订单(" + dto.getOrderNo() + ")验价失败：" + response.getMsg(), null, EntDepartment.DEPT_TICKETING);
      return;
    }


    for (PnrInfoFn infoPrice : response.getPnrInfo().getFns()) {
      if (infoPrice.getPrice() <= adultPrice.getParValue() &&
              infoPrice.getTax() == adultPrice.getTax()) {
        //验价成功
        String remark = infoPrice.getPrice() + " <= " + adultPrice.getParValue();
        flightOrderDao.updateFlightOrderPriceChecked(enterpriseId, orderId, username, 1, "编码验价相符(调用记录ID：" + response.getQueryId() + ", " + remark + ")");

        if (adultPrice.getPrice() < 0.001) {
          //补齐price
          FlightOrderRequestDto requestDto = new FlightOrderRequestDto();
          requestDto.setPsgCount(dto.getPsgCount());
          requestDto.setAdultCount(dto.getAdultCount());
          requestDto.setAdultPrice(adultPrice);

          adultPrice.setPrice(adultPrice.getParValue());

          flightOrderDao.updatePrice(enterpriseId, username, orderId, requestDto);
        }

        return;
      }
    }

    //验价未通过
    String remark = "编码验价失败(调用记录ID：" + response.getQueryId();
    if (response.getPnrInfo().getFns().size() == 1) {
      PnrInfoFn infoPrice = response.getPnrInfo().getFns().get(0);
      remark += ", " + infoPrice.getPrice() + " != " + adultPrice.getParValue();
    }
    remark += ")";
    flightOrderDao.updateFlightOrderPriceChecked(enterpriseId, orderId, username, 4, remark);
    //刷新编码内容
    refreshFlightOrderPnrDetail(enterpriseId, username, o);

  }

  /**
   * 校验机票订单价格（大客户政策订单）, 此处为允许客户知道是大客户价，一般是指从前台页面下单时选择的
   */
  private void executeCheckFlightOrderPriceWithPolicyCode(final FlightOrder orderInfo, final String username) {
    //私有运价（有policyCode的订单），按照公式计算留扣，并回写机票订单
    //校验policyCode是否可用
    final int enterpriseId = orderInfo.getEnterpriseId();
    final int orderId = orderInfo.getId();
    final String pnrNo = orderInfo.getPnrNo();
    final String policyCode = StringUtils.trimToEmpty(orderInfo.getPolicyCode());

    if (policyCode.length() == 0) {
      return;
    }

    FlightKeyCustomerPolicy policy = keyCustomerPolicyDao.searchFlightKeyCustomerPolicy(enterpriseId, policyCode);
    if (policy == null) {
      flightOrderDao.updateFlightOrderPriceChecked(enterpriseId, orderId, username, 4, "机票私有政策不存在(" + policyCode + ")");
      return;
    }

    if (policy.getStatus() == 0) {
      flightOrderDao.updateFlightOrderPriceChecked(enterpriseId, orderId, username, 4, "机票私有政策已停用(" + policyCode + ")");
      return;
    }

    if (policy.getEffectDate().getTime() > DateUtil.getCurDate().getTime()) {
      flightOrderDao.updateFlightOrderPriceChecked(enterpriseId, orderId, username, 4, "机票私有政策未生效(" + policyCode + ")");
      return;
    }

    if (policy.getExpireDate().getTime() < DateUtil.getCurDate().getTime()) {
      flightOrderDao.updateFlightOrderPriceChecked(enterpriseId, orderId, username, 4, "机票私有政策已失效(" + policyCode + ")");
      return;
    }

    //查找留扣信息
    final FlightOrder o = flightOrderDao.getOrderDetailById(orderId);
    final FlightOrderResponseDto dto = FlightOrderResponseDto.createByFlightOrder(o);
    final int customerEnterpriseId = o.getCustomer().getCustomerEnterpriseId();

    CustomerFlightDiscount customerFlightDiscount = keyCustomerPolicyDao.searchCustomerFlightDiscount(enterpriseId, customerEnterpriseId, policyCode);
    if (customerFlightDiscount == null) {
      flightOrderDao.updateFlightOrderPriceChecked(enterpriseId, orderId, username, 4, "未找到可用的机票私有政策(" + policyCode + ")");
      doSendAlert(enterpriseId, "订单(" + dto.getOrderNo() + ")私有运价验价失败：未找到可用的机票私有政策(" + policyCode + ")", null, EntDepartment.DEPT_TICKETING);
      return;
    }

    PnrPatResponse response = execApiPatPnr(enterpriseId, username, pnrNo, policy.getKeyCustomerCode(), o.getOrderNo(), "编码私有运价验价");
    if (response.getCode() != 1) {
      doSendAlert(enterpriseId, "订单(" + dto.getOrderNo() + ")私有运价验价时: " + response.getMsg(), null, EntDepartment.DEPT_TICKETING);
      return;
    }

    if (response.getPnrInfo().getFns().isEmpty()) {
      //验价未通过
      flightOrderDao.updateFlightOrderPriceChecked(enterpriseId, orderId, username, 4, "编码私有运价验价(调用记录ID：" + response.getQueryId() + ")");
      doSendAlert(enterpriseId, "订单(" + dto.getOrderNo() + ")私有运价验价时: Fn is empty.", null, EntDepartment.DEPT_TICKETING);
      return;
    }

    //如果有多个，暂时找最便宜的
    PnrInfoFn priceInfo = null;
    for (PnrInfoFn p : response.getPnrInfo().getFns()) {
      if (priceInfo == null || priceInfo.getPrice() > p.getPrice()) {
        priceInfo = p;
      }
    }

    if (priceInfo == null) {
      return;
    }

    //税
    final int tax = (int) priceInfo.getTax();

    double parValue = priceInfo.getPrice() * (100 + customerFlightDiscount.getCommissionRateRetained()) / 100.0;
    parValue = DecimalUtil.keepScale2(parValue);
    double commission = parValue - priceInfo.getPrice();
    commission = DecimalUtil.keepScale2(commission);

    FlightOrderRequestDto newDto = new FlightOrderRequestDto();
    newDto.setPsgCount(dto.getPsgCount());
    newDto.setAdultCount(dto.getAdultCount());

    BasePriceDto newAdultPrice = new BasePriceDto();
    newAdultPrice.setPrice(priceInfo.getPrice());
    newAdultPrice.setParValue(parValue);
    newAdultPrice.setTax(tax);
    newAdultPrice.setCommission(commission);

    newDto.setAdultPrice(newAdultPrice);

    int retCode = flightOrderDao.updatePrice(enterpriseId, username, orderId, newDto);
    if (retCode != 1) {
      return;
    }

    //验价成功
    if (flightOrderDao.updateFlightOrderPriceChecked(enterpriseId, orderId, username, 1, "编码私有运价验价成功(调用记录ID：" + response.getQueryId() + ")") == 1) {
      //更新订单中的大客户代码
      flightOrderDao.updateFlightOrderKeyCustomerNo(enterpriseId, username, orderId, policy.getKeyCustomerCode());
    }
    //刷新编码内容
    refreshFlightOrderPnrDetail(enterpriseId, username, o);

  }

  /**
   * 对于非大客户价格的订单，是否考虑自动转为大客户订单？
   * 自动将订单从普通政策的转化为大客户政策的
   * @return 1 - 成功
   *          0 - 不成功
   */
  private BaseDto convertFlightOrderNormalPrice2PolicyCode(final FlightOrder orderInfo, final String username) {
    var result = new BaseDto();

    //暂时只考虑非行程单、单程、一个成人的订单
    if (orderInfo.getItineraryType() == 1
            || orderInfo.getSegCount() != 1
            || orderInfo.getAdultCount() != 1) {
      result.setErrcode(-1);
      result.setErrmsg("非符合要求(非行程单、单程、一个成人)的订单");
      return result;
    }

    //查看是否有匹配的大客户政策
    final int enterpriseId = orderInfo.getEnterpriseId();
    final String carrier = orderInfo.getFlights().get(0).getCarrierCode();
    final String subclass = orderInfo.getFlights().get(0).getFlight().getSubclass();

    List<FlightKeyCustomerPolicy> list = getFlightKeyCustomerPolicy(enterpriseId, carrier, subclass);
    if (list.isEmpty()) {
      result.setErrcode(-2);
      result.setErrmsg("无匹配机票大客户政策");
      return result;
    }

    int retCode = 0;
    for (FlightKeyCustomerPolicy policy : list) {
      //先匹配白名单政策
      if (policy.getWhiteList() != 1) {
        continue;
      }
      //匹配上了，可以将该订单转化为 大客户政策订单
      retCode = applyPolicy2FlightOrder(orderInfo, policy, username);
      if (retCode > 0) {
        //转换成功
        break;
      }
    }

    if (retCode == 0) {
      //白名单政策未匹配成功，则匹配非白名单
      for (FlightKeyCustomerPolicy policy : list) {
        //匹配上了，可以将该订单转化为 大客户政策订单
        retCode = applyPolicy2FlightOrder(orderInfo, policy, username);
        if (retCode > 0) {
          //转换成功
          break;
        }
      }
    }

    if (retCode < 0) {
      result.setErrcode(retCode);
    }

    return result;

  }

  /**
   * 自动设置机票订单的服务费
   */
  private void setFlightOrderTicketingFee(final String username, final int orderId) {
    FlightOrder flightOrder = flightOrderDao.getOrderDetailById(orderId);
    final int enterpriseId = flightOrder.getEnterpriseId();
    FlightOrderResponseDto dto = FlightOrderResponseDto.createByFlightOrder(flightOrder);

    if (dto.getAdultPrice().getServiceCharge() > 0) {
      return;
    }
    // 订单里没有服务费，则自动设置服务费
    final int ticketingFee = getTicketingServiceCharge(flightOrder);
    if (ticketingFee <= 0) {
      return;
    }

    //需要设置服务费
    FlightOrderRequestDto requestDto = new FlightOrderRequestDto();
    requestDto.setPsgCount(dto.getPsgCount());
    requestDto.setAdultCount(dto.getAdultCount());

    requestDto.setAdultPrice(dto.getAdultPrice());
    requestDto.getAdultPrice().setServiceCharge(ticketingFee);
    if (requestDto.getAdultPrice().getPrice() < 0.001) {
      requestDto.getAdultPrice().setPrice(requestDto.getAdultPrice().getParValue());
    }

    flightOrderDao.updatePrice(enterpriseId, username, orderId, requestDto);
  }

  /**
   * 将指定的私有政策应用到机票订单上
   * @param orderInfo 订单信息
   * @param policy 私有政策
   * @param username 用户名
   * @return 负数 - 失败
   *           1 - 成功
   */
  private int applyPolicy2FlightOrder(FlightOrder orderInfo, FlightKeyCustomerPolicy policy, final String username) {
    final int enterpriseId = orderInfo.getEnterpriseId();
    if (policy.getWhiteList() == 1) {
      //需要白名单, 检验和该客户适用的政策是否匹配
      final String psgName = orderInfo.getPassengers().get(0).getPsgName();
      final int idType = orderInfo.getPassengers().get(0).getIdType();
      final String idNo = orderInfo.getPassengers().get(0).getIdNo();

      int retCode = getFlightKeyCustomerPolicyWithWhiteList(enterpriseId, psgName, idType, idNo, policy.getPolicyCode());
      if (retCode < 0) {
        return retCode;
      }
    }

    //匹配上了，可以将该订单转化为 大客户政策订单
    return execConvertFlightOrderNormalPrice2PolicyCode(orderInfo, username, policy);
  }

  private PnrCreateRequest createPnrCreateRequest(final FlightOrder orderInfo) {
    PnrCreateRequest request = new PnrCreateRequest();
    request.getBaseInfo().setPnrType("ADU");

    if (orderInfo.getIntlTicket() == 1) {
      //国际
      request.getBaseInfo().setJourneyType(2);
    }
    //增加CT PHONE
    request.getBaseInfo().setLinkPhone(orderInfo.getLinkPhone());

    //OSI ctct
    CreatePnrPhone ctPhone = new CreatePnrPhone();
    ctPhone.setPhone(orderInfo.getLinkPhone());
    request.getPhones().add(ctPhone);

    for (FlightOrderPassenger psgOrder : orderInfo.getPassengers()) {
      CreatePnrPassenger psg = new CreatePnrPassenger();
      psg.setName(psgOrder.getName());
      psg.setIdNo(psgOrder.getIdNo());
      //TODO 设置ID TYPE
      if (psgOrder.getMobile().length() > 0) {
        psg.setPhone(psgOrder.getMobile());
      }

      //如果是国际票，就需要多传几个参数
      if (orderInfo.getIntlTicket() == 1) {
        psg.setBirthday(DateUtil.formatDate(psgOrder.getBirthday(), DATE_YYYY_MM_DD));
        psg.setCountryCode(psgOrder.getCountryCode());
        psg.setIdIssueNation(psgOrder.getIdIssueNation());
        psg.setIdValidDate(DateUtil.formatDate(psgOrder.getIdValidDate(), DATE_YYYY_MM_DD));
        if (psgOrder.getGender() != null) {
          if (psgOrder.getGender() == GENDER_MALE) {
            psg.setGender("M");
          } else if (psgOrder.getGender() == GENDER_FEMALE) {
            psg.setGender("F");
          }
        }
      }

      request.getPassengers().add(psg);
    }

    for (FlightOrderFlight fltOrder : orderInfo.getFlights()) {
      CreatePnrFlight flt = new CreatePnrFlight();
      flt.setDport(fltOrder.getFlight().getDport());
      flt.setAport(fltOrder.getFlight().getAport());
      flt.setDdate(fltOrder.getFlight().getDdate());
      flt.setFlightNo(fltOrder.getFlight().getFlightNo().substring(2));
      flt.setCarrier(fltOrder.getFlight().getFlightNo().substring(0, 2));
      flt.setSubclass(fltOrder.getFlight().getSubclass());
      request.getFlights().add(flt);
    }

    return request;
  }

  /**
   * 将票面价修改为大客户价，将票面价和销售价的差额填到自定义代理费中，然后进入自动出票流程
   * @param orderInfo 订单信息
   * @param username 用户名
   * @param policy 政策信息
   * @return 负数 - 转换失败
   *          1 - 转换成功
   */
  private int execConvertFlightOrderNormalPrice2PolicyCode(final FlightOrder orderInfo,
                                                           final String username,
                                                           FlightKeyCustomerPolicy policy) {
    //私有运价（有policyCode的订单），按照公式计算留扣，并回写机票订单
    //校验policyCode是否可用
    final int enterpriseId = orderInfo.getEnterpriseId();
    final int orderId = orderInfo.getId();
    final String pnrNo = orderInfo.getPnrNo();

    if (policy.getStatus() == 0) {
      //机票私有政策已停用
      return -11;
    }

    if (policy.getEffectDate().getTime() > DateUtil.getCurDate().getTime()) {
      //机票私有政策未生效
      return -12;
    }

    if (policy.getExpireDate().getTime() < DateUtil.getCurDate().getTime()) {
      return -13;
    }

    if (StringUtils.isBlank(policy.getKeyCustomerCode())) {
      doSendAlert(enterpriseId, "Policy(" + policy.getId() + ")'s code is empty.", "Dai", 0);
      return -14;
    }

    //查找留扣信息
    final FlightOrderResponseDto dto = FlightOrderResponseDto.createByFlightOrder(orderInfo);

    RtpnrResponse response0 = execApiRt(enterpriseId, username, pnrNo, policy.getKeyCustomerCode(), dto.getOrderNo(), "获取编码私有运价");
    if (response0.getPnrInfo().getPrices().isEmpty()) {
      //验价未通过
      return -15;
    }

    //如果有多个，暂时找最便宜的
    PnrInfoPrice priceInfo = null;
    for (PnrInfoPrice price : response0.getPnrInfo().getPrices()) {
      if (priceInfo == null || priceInfo.getPrice() > price.getPrice()) {
        priceInfo = price;
      }
    }

    if (priceInfo == null) {
      return -16;
    }

    //税
    final int tax = priceInfo.getAirportTax() + priceInfo.getFuelSurcharge();
    final double parValue = orderInfo.getParvalue();
    final double commission2 = DecimalUtil.keepScale2(parValue - priceInfo.getPrice());
    if (commission2 <= 0) {
      doSendAlert(enterpriseId, "机票订单(" + orderInfo.getOrderNo() + ")试匹配大客户政策(" + policy.getId() + ", "
              + policy.getPolicyCode() + ", "
              + policy.getKeyCustomerCode() + ")时未显示优惠。", null, EntDepartment.DEPT_CUSTOMER_SERVICE);
      return -17;
    }

    //大客户代码验价成功，调用PAT PNR接口，将运价保存到PNR中
    PnrPatResponse response = execApiPatPnr(enterpriseId, username, pnrNo, policy.getKeyCustomerCode(), dto.getOrderNo(), "编码PAT:A验价并保存");

    if (response.getPnrInfo() == null
            || response.getPnrInfo().getFns().isEmpty()) {
      //验价未通过
      String remark = "编码(" + pnrNo + ")验价失败(调用记录ID：" + response.getQueryId() + ")";
      doSendAlert(enterpriseId, remark + ": " + response.getMsg(), "Dai", 0);
      flightOrderDao.updateFlightOrderPriceChecked(enterpriseId, orderId, username, 4, remark);
    }

    FlightOrderRequestDto newDto = new FlightOrderRequestDto();
    newDto.setPsgCount(dto.getPsgCount());
    newDto.setAdultCount(dto.getAdultCount());

    BasePriceDto newAdultPrice = new BasePriceDto();
    newAdultPrice.setPrice(priceInfo.getPrice());
    newAdultPrice.setParValue(parValue);
    newAdultPrice.setTax(tax);
    newAdultPrice.setCommission2(commission2);

    newDto.setAdultPrice(newAdultPrice);

    int retCode = flightOrderDao.updatePrice(enterpriseId, username, orderId, newDto);
    if (retCode != 1) {
      return -18;
    }

    //验价成功
    if (flightOrderDao.updateFlightOrderPriceChecked(enterpriseId, orderId, username, 1, "编码私有运价验价成功(调用记录ID：" + response.getQueryId() + ")") == 1) {
      //更新订单中的大客户代码
      flightOrderDao.updateFlightOrderKeyCustomerNo(enterpriseId, username, orderId, policy.getKeyCustomerCode());
    }
    //刷新编码内容
    refreshFlightOrderPnrDetail(enterpriseId, username, orderInfo);

    return retCode;
  }

  /**
   * 调用接口，将PAT:A运价保存到PNR中
   * 注意： PnrPatResponse的价格是放在fn中的
   */
  private PnrPatResponse execApiPatPnr(final int enterpriseId, final String username, final String pnrNo,
                                       final String keyCustomerCode, final String orderNo, final String remark) {
    PnrPatRequest patRequest = new PnrPatRequest();
    patRequest.setPnrNo(pnrNo);
    if (keyCustomerCode != null) {
      patRequest.setCustomerCode(keyCustomerCode);
    }

    final int queryId = userDao.createEtermCmdLog(enterpriseId, username, remark, orderNo);

    SzkjEtermService szkjEtermService = createSzkjEtermService(enterpriseId);

    PnrPatResponse response = szkjEtermService.patPnr(getEtermUsername(enterpriseId), getEtermPassword(enterpriseId), queryId, patRequest);
    userDao.updateEtermCmdLog(queryId, response.getRequestStr(), response.getResponseStr());

    return response;
  }

  private void refreshFlightOrderPnrDetail(int enterpriseId, String username, FlightOrder flightOrder) {
    doRefreshFlightOrderPnrDetail(enterpriseId, username, flightOrder.getId(), flightOrder.getOrderNo(), flightOrder.getPnrNo());
  }

  /**
   * 查找符合条件的私有政策，并明确设置了是否需要白名单
   */
  private List<FlightKeyCustomerPolicy> getFlightKeyCustomerPolicy(int enterpriseId, String carrier, String subclass) {
    List<FlightKeyCustomerPolicy> list = new ArrayList<>();

    List<FlightKeyCustomerPolicy> policies = keyCustomerPolicyDao.search(enterpriseId, carrier);
    for (FlightKeyCustomerPolicy p : policies) {
      if (p.getSubclasses().contains(subclass) && p.getWhiteList() != -1) {
        //该政策确定是否需要白名单
        list.add(p);
      }
    }

    return list;
  }

  /**
   * 白名单政策是否和当前乘机人匹配
   * @return 0-匹配成功，
   *  负数 - 匹配失败
   */
  private int getFlightKeyCustomerPolicyWithWhiteList(final int enterpriseId, final String psgName, final int idType,
                                                      final String idNo, final String policyCode) {
    Passenger psgInfo = null;
    List<Passenger> passengers = passengerDao.searchByName(enterpriseId, 0, psgName);
    for (Passenger psg : passengers) {
      //判断证件是否匹配
      for (PassengerIdInfo idInfo : psg.getIdInfos()) {
        if (idInfo.getIdNo().equalsIgnoreCase(idNo)
                && idInfo.getIdType() == idType) {
          psgInfo = psg;
          break;
        }
      }
      if (psgInfo != null) {
        break;
      }
    }

    //未找到匹配的乘机人信息
    if (psgInfo == null) {
      return -2;
    }

    //查找该乘机人适用的机票政策
    FlightKeyCustomerPolicy policy1 = null;
    List<PassengerFlightPolicy> flightPolicies = enterpriseDao.searchPassengerFlightPolicies(enterpriseId, psgInfo.getId());
    for (PassengerFlightPolicy flightPolicy : flightPolicies) {
      if (flightPolicy.getPolicyInfo().getPolicyCode().equalsIgnoreCase(policyCode)) {
        policy1 = flightPolicy.getPolicyInfo();
        break;
      }
    }

    //未在乘机人适用的机票政策中找到匹配的
    if (policy1 == null) {
      return -3;
    } else {
      return 0;
    }
  }
}
